'''
Created on 2014-12-1

@author: rake
'''
import wx
import wx.xrc as xrc
import wx.dataview as dv
from exception import exceptionHandler as ehandler
from dbOper import db2oper as db2
from dbOper import DBObject as dbobj
from sqlalchemy.sql.functions import coalesce
import thread


class ObjectSearchPanel(wx.Panel):
    '''
    ObjectSearchPanel:the panel search db2 object
    '''
    def __init__(self, parent, appContext):
        '''
        Constructor
        '''
        wx.Panel.__init__(self,parent=parent)
        self.__appContext = appContext
        
        pre = wx.PrePanel()
        res = xrc.EmptyXmlResource()
        res.Load("xrc_file/objSearchPanel.xrc")
        res.LoadOnPanel(pre, parent, "panel_search")
        self.PostCreate(pre)
        
        self.ctrlInputSearch = xrc.XRCCTRL(self, "text_searchInput", wx.TextCtrl)
        self.panelSearchResult = xrc.XRCCTRL(self, "panelSearchResult", wx.Panel)
        sizerBox1 = wx.BoxSizer(wx.VERTICAL)
        self.dvSearchResult = dv.DataViewCtrl(self.panelSearchResult,
                                              style=wx.BORDER_THEME
                                               | dv.DV_ROW_LINES # nice alternating bg colors
                                               #| dv.DV_HORIZ_RULES
                                               | dv.DV_VERT_RULES 
                                               | dv.DV_SINGLE
                                               )
        self.dvModel = None
        sizerBox1.Add(self.dvSearchResult, 1, wx.EXPAND | wx.ALL, 0)
        self.panelSearchResult.SetSizer(sizerBox1)
        self.__bindEvent()
        pass
    
    @property
    def appContext(self):return self.__appContext
    
    def __bindEvent(self):
        self.Bind(wx.EVT_BUTTON, self.evt_btn_search, id=xrc.XRCID('bbtn_search'))
        self.Bind(dv.EVT_DATAVIEW_ITEM_CONTEXT_MENU, self.evt_context_menu)
        pass
    
    def __dealSearchWords(self, scriptStr):
        sword = self.ctrlInputSearch.GetValue()
        swords = sword.split(";")
        i = 1
        for val in swords:
            scriptStr = scriptStr.replace("#{"+str(i)+"}",val.upper())
            i = i + 1
            pass
        return scriptStr
    
    @staticmethod
    @ehandler.ExceptHandler
    def evt_btn_search_run(page, catalog, script):
        seperator = ";"
        autoCommit = True
        res = db2.execScriptsDb2(catalog, script, \
                             seperator, autoCommit)
        res1 = res[0]
        page.dvSearchResult.ClearColumns()
        page.dvModel = CustomTreeListModel(res1)
        page.dvSearchResult.AssociateModel(page.dvModel)
        
        columnNames = res1[1]
        for i in range(len(columnNames)):
            page.dvSearchResult.AppendTextColumn(columnNames[i], i, width=150)
            pass
        
        pass
    
    def evt_btn_search(self, event):
        catalog = self.appContext['mainFrame'].getSelectedCatalog()
        
        allSolution = self.appContext["searchSolution"]["default"]["all"]
        script = self.__dealSearchWords(allSolution["script"])
        
        thread.start_new_thread(ObjectSearchPanel.evt_btn_search_run, (self, catalog, script))
        pass
    
    @staticmethod
    @ehandler.ExceptHandler
    def event_menu_action(dbo, actionName, params, context):
        def do_action(event):
            dbo.doAction(actionName, params, context)
            pass
        return do_action
    
    @ehandler.ExceptHandler
    def evt_context_menu(self, event):
        selection = self.dvSearchResult.GetSelection()
        if not selection:
            return
        allSolution = self.appContext["searchSolution"]["default"]["all"]
        objname_column_idx = allSolution["objname_column_idx"]
        type_column_idx = allSolution["type_column_idx"]
        type_mapping = allSolution["type_mapping"]
        data = self.dvModel.ItemToObject(selection)
        object_type = data.val[type_column_idx]
        object_type2 = type_mapping[object_type]
        objectname = data.val[objname_column_idx]
        dbo = getattr(dbobj, object_type2)(objectname)
        
        # make a menu
        menu = wx.Menu()
        
        for akey in dbo.actions.keys():
            if not hasattr(self, "popupID_"+akey):
                setattr(self, "popupID_"+akey, wx.NewId())
                pass
            self.Bind(wx.EVT_MENU, \
                      ObjectSearchPanel.event_menu_action(dbo, akey, {}, self.appContext), \
                      id=getattr(self, "popupID_"+akey))
            menu.Append(getattr(self, "popupID_"+akey), akey)
            pass
        # Popup the menu.  If an item is selected then its handler
        # will be called before PopupMenu returns.
        self.PopupMenu(menu)
        menu.Destroy()
        pass
    
#method coalesce
def coalesce(x) : return x if x is not None else ''

#=|start of class CustomTreeListModel|================================================
class CustomTreeListModel(dv.PyDataViewModel):
    
    def __init__(self, searchResult):
        dv.PyDataViewModel.__init__(self)
        self.data = searchResult[0]
        self.columnNames = searchResult[1]
        self.dataTypes = searchResult[2]
        self.treeData = CustomTreeListModel.transferDataToTree(self.columnNames, self.data)
        self.objmapper.UseWeakRefs(True)
        
    class treeElem(object):
        def __init__(self, val, children, level, parent):
            object.__init__(self)
            self.val = val
            self.children = children
            self.level = level
            self.parent = parent
            
    @staticmethod
    def transferDataToTree(columns, data, val=None, level=0, parent = None):
        res = CustomTreeListModel.treeElem(val, list(), level, parent)
        tmp = None
        tmpobj = None
        tmplst = list()
        i = 0
        for x in data:
            if i==0:
                pass
            elif x[level] != tmp :
                if len(tmplst)<=1:
                    res.children.append(CustomTreeListModel.treeElem(tmpobj, list(), level, res))
                else :
                    res.children.append(CustomTreeListModel.transferDataToTree(columns, tmplst, tmp, level+1, res))
                tmplst = list()
                
            tmpobj = x 
            tmplst.append(tmpobj)
            tmp = x[level]
            i = i + 1
            pass
        
        if len(tmplst)<=1:
            res.children.append(CustomTreeListModel.treeElem(tmpobj, list(), level, res))
        else :
            res.children.append(CustomTreeListModel.transferDataToTree(columns, tmplst, tmp, level+1, res))

        return res
    
    # Report how many columns this model provides data for.
    def GetColumnCount(self):
        return len(self.columnNames)

    # Map the data column numbers to the data type
    def GetColumnType(self, col):
        return self.dataTypes[col]
    
    def GetChildren(self, parent, children):  
        if not parent:
            for val in self.treeData.children:
                children.append(self.ObjectToItem(val))
            return len(self.treeData.children)
        
        node = self.ItemToObject(parent)
        for val in node.children:
            children.append(self.ObjectToItem(val))
        return len(node.children)

    def IsContainer(self, item):
        # Return True if the item has children, False otherwise.
        # The hidden root is a container
        if not item:
            return True
        
        node = self.ItemToObject(item)
        return True if len(node.children)>0 else False

    #def HasContainerColumns(self, item):
    #    self.log.write('HasContainerColumns\n')
    #    return True

    
    def GetParent(self, item):
        # Return the item which is this item's parent.
        if not item:
            return dv.NullDataViewItem

        node = self.ItemToObject(item)        
        return self.ObjectToItem(node.parent)
        
    def GetValue(self, item, col):
        # Return the value to be displayed for this item and column. For this
        # example we'll just pull the values from the data objects we
        # associated with the items in GetChildren.
        
        # Fetch the data object for this item.
        data = self.ItemToObject(item)
        if isinstance(data, CustomTreeListModel.treeElem):
            if len(data.children)>0:
                res = coalesce(data.val)
            elif len(data.children)==0:
                res = coalesce(data.val[col])
        else :
            raise TypeError("illegal type for data")
        return res

    def GetAttr(self, item, col, attr):
#         node = self.ItemToObject(item)
#         if isinstance(node, CustomTreeListModel.treeElem):
#             attr.SetColour('blue')
#             attr.SetBold(True)
#             return True
        return False
    
    
    def SetValue(self, value, item, col):
        # We're not allowing edits in column zero (see below) so we just need
        # to deal with Song objects and cols 1 - 5
        node = self.ItemToObject(item)
        if isinstance(node, CustomTreeListModel.treeElem):
            if len(node.children)>0:
                node[col] = value
                pass   
    